#ifndef __IMAGE_STREAM_DOT_HPP__
#define __IMAGE_STREAM_DOT_HPP__
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <assert.h>
#include <sys/stat.h>
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <fstream>
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
#include <memline.hpp>
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
uint64_t StatFileSize( const char * file_name );
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
#define ZEST_FILE_ABSTRACTION_PARENT_CLASS BufferedIFStreamProgress
// #define ZEST_FILE_ABSTRACTION_PARENT_CLASS IFStreamProgress
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
class StatFile {
protected:
    int fd;
    
    // track the file size; (f)loat and i(n)teger
    float    fsize;
    uint64_t nsize;
public:
    StatFile( void ) : fd( 0 ), fsize( 0.0 ), nsize( 0ULL ) {}
    bool open( const char * file_name, const bool sync );
    inline bool open ( const char * file_name ) {
        return open( file_name, false );
    }
    inline bool close( void ) {
        if( fd ) {
            ::close( fd );
        }
        return true;
    }
    inline uint64_t read( char * buf, uint64_t len ) const { return ::read( fd, buf, len ); }
    inline uint64_t size( void ) const { return nsize; }
};

class IFStreamProgress : public std::ifstream {
    
    // only one thread allowed to call read at a time
    pthread_mutex_t * disklock;
    
    // number of bytes read out of this object
    uint64_t bytesOut;
    
protected:
    // track the file size; (f)loat and i(n)teger
    float    fsize;
    uint64_t nsize;
    
    size_t ReadAndCountBytes( char * buf, size_t len );
public:
    bool open( const char * file_name, pthread_mutex_t * _disklock, const bool sync );
    bool ReadTrackProgress( char * dst, size_t len );
    
    inline float pcnt_through  ( void ) const { return 100.0 * float( bytesOut ) / fsize; }
    
    uint64_t BufferSize( void ) const { return 0ULL; }
    inline uint64_t size( void ) const { return nsize; }
};

class BufferedIFStreamProgress : public StatFile {
    
    // only one thread allowed to call read at a time
    pthread_mutex_t * disklock;
    
    // 128 MB: ~0.5 to ~2.0 seconds depending on magnetic disk bandwidth
    static const uint64_t mb = 128; 
    static const uint64_t buffsize = mb * 1024 * 1024;

    // the buffer    
    char buf[ buffsize ];
    char * pbuf;
    bool bufferwarn;
    
    // number of bytes read out of this object
    // number of bytes read into buffer
    uint64_t bytesOut;
    uint64_t bytesIn;
    
    // pos: position in buffer
    // max: index to last valid byte in buffer
    uint32_t pos;
    uint32_t max;
    
    size_t BufferIn( void );
    size_t ReadAndCountBytes( char * buf, size_t len );
public:
    bool open( const char * file_name, pthread_mutex_t * _disklock, const bool sync );
    bool ReadTrackProgress( char * dst, size_t len );
    
    inline float pcnt_through  ( void ) const { return 100.0 * float( bytesOut ) / fsize; }
    
    uint64_t BufferSize( void ) const { return buffsize; }
};

class DRAMImageStream : public ZEST_FILE_ABSTRACTION_PARENT_CLASS {
public:
    // read 64 bytes into line_t object
    // return false if no more data
    bool read_line( line_t * data ) {
        return ReadTrackProgress( data->RawCharPtr(), lineSize );
    }
};

class RCImageStream   : public ZEST_FILE_ABSTRACTION_PARENT_CLASS {
public:
    // read two page reference counts from binary file
    bool read_page_refs( uint32_t * krc, int32_t * urc ) {

        uint64_t buf;
        
        if( ReadTrackProgress( (char*) &buf, sizeof( uint64_t ) ) ) {
            
            *krc = uint32_t( buf & 0xFFFFFFFFULL );
            *urc =  int32_t( buf >> 32ULL );

            return true;
        }
        
        *krc =  0;
        *urc = -1;
        
        return false;        
    }
};

#endif
